#!/usr/bin/python3 -u
# -*- coding: utf-8 -*-
# WARNING: python -u means unbuffered I/O. Without it the messages are
# passed to the parent asynchronously which looks bad in clients.

from subprocess import Popen, PIPE
import sys
import os
import getopt

GETTEXT_PROGNAME = "@PACKAGE@"
import locale
import gettext

_ = lambda x: gettext.gettext(x)


verbose = 0

def log_warning(s):
    sys.stderr.write("%s\n" % s)

def log1(message):
    if verbose > 0:
        log_warning(message)

def log2(message):
    if verbose > 1:
        log_warning(message)

def error_msg(s):
    sys.stderr.write("%s\n" % s)

def error_msg_and_die(s):
    sys.stderr.write("%s\n" % s)
    sys.exit(1)

def xopen(name, mode):
    try:
        r = open(name, mode)
    except IOError as ex:
        error_msg_and_die("Can't open '%s': %s" % (name, ex))
    return r


def init_gettext():
    try:
        locale.setlocale(locale.LC_ALL, "")
    except locale.Error:
        os.environ['LC_ALL'] = 'C'
        locale.setlocale(locale.LC_ALL, "")
    gettext.bindtextdomain(GETTEXT_PROGNAME, "@localedir@")
    gettext.textdomain(GETTEXT_PROGNAME)

def extract_info_from_core(coredump_name):
    """
    Extracts builds with filenames,
    Returns a list of tuples (build_id, filename)
    """
    #OFFSET = 0
    BUILD_ID = 1
    LIBRARY = 2
    #SEP = 3
    EXECUTABLE = 4

    log_warning(_("Analyzing coredump '%s'") % coredump_name)
    eu_unstrip_OUT = Popen(["eu-unstrip","--core=%s" % coredump_name, "-n"], stdout=PIPE, bufsize=-1, universal_newlines=True).communicate()[0]

    # we failed to get build ids from the core -> die
    if not eu_unstrip_OUT:
        error_msg_and_die("Can't get build ids from %s" % coredump_name)

    lines = eu_unstrip_OUT.split('\n')
    # using set ensures the unique values
    build_ids = set()
    libraries = set()

    for line in lines:
        b_ids_line = line.split()
        if len(b_ids_line) >= EXECUTABLE:
            # [exe] -> the executable itself
            # linux-vdso.so.1 -> Virtual Dynamic Shared Object
            # linux-gate.so.1 -> the same as vdso
            # See https://bugzilla.redhat.com/show_bug.cgi?id=706969
            # "Please split kernel debuginfo packages so that VDSO debuginfos are separate" -
            # we might want to remove this special-casing later.
            if b_ids_line[BUILD_ID] == '-':
                log_warning(_("Missing build id: %s" % b_ids_line[EXECUTABLE]))
            elif ((len(b_ids_line) == EXECUTABLE) or (b_ids_line[EXECUTABLE] not in ["linux-vdso.so.1", "linux-gate.so.1"])):
                build_id = b_ids_line[BUILD_ID].split('@')[0]
                build_ids.add(build_id)
                library = b_ids_line[LIBRARY]
                libraries.add(library)
            else:
                log2("skipping line '%s'" % line)
    log1("Found %i build_ids" % len(build_ids))
    log1("Found %i libs" % len(libraries))
    return build_ids

if __name__ == "__main__":
    # localization
    init_gettext()

    ABRT_VERBOSE = os.getenv("ABRT_VERBOSE")
    if (ABRT_VERBOSE):
        try:
            verbose = int(ABRT_VERBOSE)
        except:
            pass

    progname = os.path.basename(sys.argv[0])
    help_text = _("Usage: %s [-v] [-o OUTFILE] -c COREFILE") % progname
    try:
        opts, args = getopt.getopt(sys.argv[1:], "vhc:o:", ["help", "core="])
    except getopt.GetoptError as err:
        error_msg(err) # prints something like "option -a not recognized"
        error_msg_and_die(help_text)

    core = None
    opt_o = None

    for opt, arg in opts:
        if opt in ("-h", "--help"):
            print(help_text)
            exit(0)
        elif opt == "-v":
            verbose += 1
        elif opt == "-o":
            opt_o = arg
        elif opt in ("-c", "--core"):
            core = arg

    if not core:
        error_msg(_("COREFILE is not specified"))
        error_msg_and_die(help_text)

    b_ids = extract_info_from_core(core)

    try:
        # Note that we open -o FILE only when we reach the point
        # when we are definitely going to write something to it
        outfile = sys.stdout
        outname = opt_o
        # Make sure the file is readable for all
        oldmask = os.umask(0o002)
        for bid in b_ids:
            if outname:
                outfile = xopen(outname, "w")
                outname = None
            outfile.write("%s\n" % bid)
        outfile.close()
        os.umask(oldmask)
    except IOError as e:
        if not opt_o:
            opt_o = "<stdout>"
        error_msg_and_die("Error writing to '%s': %s" % (opt_o, e))
